home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 February / PCWorld_2000-02_cd.bin / Software / Servis / FFE / HELP.SWG / 0004_Windows Help (.HLP) and SHG-MRB.pas < prev   
Pascal/Delphi Source File  |  1997-05-11  |  58KB  |  1,658 lines

  1. Windows Help File Format / Annotation File Format / SHG and MRB File Format
  2.  
  3. This documentation describes the file format parsed by HELPDECO, because
  4. Microsoft did not publish the file formats used by WinHelp and MultiMedia
  5. Viewers, and created by HC30, HC31, HCP, HCRTF, HCW, MVC, MMVC and WMVC.
  6. This way it is not an official reference, but the result of many weekends
  7. of work dumping 500+ help files and trying to understand what all the bytes
  8. may mean.
  9. I would like to thank Pete Davis, who first tried to describe 'The Windows
  10. Help File Format' in Dr. Dobbs Journal, Sep/Oct 1993, and Holger Haase, who
  11. did a lot of work on picture file formats and Bent Lynggaard for the infor-
  12. mation on free lists in help files and unused bytes in B+ trees.
  13.  
  14. Revision 1: Fixed hash value calculation and |FONT, minor additions
  15. Revision 2: Transparent bitmaps, {button}, and {mci} commands
  16. Revision 3: Unknown in Paragraphinfo changed, minor additions
  17. Revision 4: CTXOMAP corrected, bitmap dimensions dpi - not PelsPerMeter
  18. Revision 5: MacroData in HotspotInfo added, Annotation file format added
  19. Revision 6: [MACROS] section / internal file |Rose added, MVB font structure
  20. Revision 7: [GROUPS] section *.GRP and [CHARTAB] section *.tbl file format
  21. Revision 8: free list, clarified TOPICPOS/TOPICOFFSET
  22. Revision 9: B+ tree unused bytes and what I found out about GID files
  23.  
  24. A help file starts with a header, the only structure at a fixed place
  25.  
  26. long Magic           0x00035F3F
  27. long DirectoryStart       offset of FILEHEADER of internal directory
  28. long FirstFreeBlock       offset of FREEHEADER or -1L if no free list
  29. long EntireFileSize       size of entire help file in bytes
  30. ----
  31. char HelpFileContent[EntireFileSize-16]   the remainder of the help file
  32.  
  33. At offset DirectoryStart the FILEHEADER of the internal directory is located
  34.  
  35. long ReservedSpace         size reserved including FILEHEADER
  36. long UsedSpace             size of internal file in bytes
  37. unsigned char FileFlags      normally 4
  38. ----
  39. char FileContent[UsedSpace]  the bytes contained in the internal file
  40. char FreeSpace[ReservedSpace-UsedSpace-9]
  41.  
  42. The FILEHEADER of the internal directory is followed by UsedSpace bytes
  43. containing the internal directory which is used to associate FileNames and
  44. FileOffsets. The directory is structured as a B+ tree.
  45. A B+ tree is made from leaf-pages and index-pages of fixed size, one of which
  46. is the root-page. All entries are contained in leaf-pages. If more entries
  47. are required than fit into a single leaf-page, index-pages are used to locate
  48. the leaf-page which contains the required entry.
  49. A B+ tree starts with a BTREEHEADER telling you the size of the B+ tree pages,
  50. the root-page, the number of levels, and the number of all entries in this
  51. B+ tree. You must follow (NLevels-1) index-pages before you reach a leaf-page.
  52.  
  53. unsigned short Magic        0x293B
  54. unsigned short Flags        bit 0x0002 always 1, bit 0x0400 1 if directory
  55. unsigned short PageSize     0x0400=1k if directory, 0x0800=2k else, or 4k
  56. char Structure[16]        string describing format of data
  57.                 'L' = long (indexed)
  58.                 'F' = NUL-terminated string (indexed)
  59.                 'i' = NUL-terminated string (indexed)
  60.                 '2' = short
  61.                 '4' = long
  62.                 'z' = NUL-terminated string
  63.                 '!' = long count value, count/8 * record
  64.                     long filenumber
  65.                     long TopicOffset
  66. short MustBeZero        0
  67. short PageSplits        number of page splits B+ tree has suffered
  68. short RootPage            page number of B+ tree root page
  69. short MustBeNegOne        0xFFFF
  70. short TotalPages        number of B+ tree pages
  71. short NLevels            number of levels of B+ tree
  72. long TotalBtreeEntries        number of entries in B+ tree
  73. ----
  74. char Page[TotalPages][PageSize] the pages the B+ tree is made of
  75.  
  76. If NLevel is greater than 1, RootPage is the page number of an index-page.
  77. Index-pages start with a BTREEINDEXHEADER and are followed by an array of
  78. BTREEINDEX structures, in case of the internal directory containing pairs
  79. of FileNames and PageNumbers.
  80. (STRINGZ is a NUL-terminated string, sizeof(STRINGZ) is strlen(string)+1).
  81. PageNumber gets you to the next page containing entries lexically starting
  82. at FileName, but less than the next FileName. PreviousPage gets you to the
  83. next page if the desired FileName is lexically before the first FileName.
  84.  
  85. unsigned short Unused     number of free bytes at end of this page
  86. short NEntries         number of entries in this index-page
  87. short PreviousPage     page number of previous page
  88. ----
  89. struct             and this is the structure of directory index-pages
  90. {
  91.     STRINGZ FileName     varying length NUL-terminated string
  92.     short PageNumber     page number of page dealing with FileName and above
  93. }
  94. DIRECTORYINDEXENTRY[NEntries]
  95.  
  96. After NLevels-1 of index-pages you will reach a leaf-page starting with a
  97. BTREENODEHEADER followed by an array of BTREELEAF structures, in case of the
  98. internal directory containing pairs of FileNames and FileOffsets.
  99. You may follow the PreviousPage entry in all NLevels-1 index-pages to reach
  100. the first leaf-page, then iterate thru all entries and use NextPage to
  101. follow the double linked list of leaf-pages until NextPage is -1 to retrieve
  102. a sorted list of all TotalBtreeEntries entries contained in the B+ tree.
  103.  
  104. unsigned short Unused     number of free bytes at end of this page
  105. short NEntries         number of entries in this leaf-page
  106. short PreviousPage     page number of previous leaf-page or -1 if first
  107. short NextPage         page number of next leaf-page or -1 if last
  108. ----
  109. struct             and this is the structure of directory leaf-pages
  110. {
  111.     STRINGZ FileName     varying length NUL-terminated string
  112.     long FileOffset     offset of FILEHEADER of internal file FileName
  113.              relative to beginning of help file
  114. }
  115. DIRECTORYLEAFENTRY[NEntries]
  116.  
  117. At offset FreeListBlock the first FREEHEADER is located. It contains
  118.  
  119. long FreeSpace         number of bytes unused, including this header
  120. long NextFreeBlock     offset of next FREEHEADER or -1L if end of list
  121. ----
  122. char Unused[FreeSpace-8] unused bytes
  123.  
  124. All unused portions of the help file are linked together using FREEHEADERs.
  125.  
  126. Now that you are able to locate the position of an internal file in the
  127. help file, let's describe what they contain. Remember that each FileOffset
  128. first takes you to the FILEHEADER of the internal file. The structures
  129. described next are located just behind this FILEHEADER.
  130.  
  131. |SYSTEM
  132.  
  133. The first one to start with is the |SYSTEM file. This is the SYSTEMHEADER,
  134. the structure of the first bytes of this internal file:
  135.  
  136. short Magic         0x036C
  137. short Minor         help file format version number
  138.              15 = HC30 Windows 3.0 help file
  139.              21 = HC31 Windows 3.1 help file
  140.              27 = WMVC/MMVC media view file
  141.              33 = MVC or HCW 4.00 Windows 95
  142. short Major         1
  143. time_t GenDate         help file created seconds after 1.1.1980, or 0
  144. unsigned short Flags     see below
  145.  
  146. Use Minor and Flags to find out how the help file was compressed:
  147. Minor <= 16         not compressed, TopicBlockSize 2k
  148. Minor > 16         Flags=0: not compressed,  TopicBlockSize 4k
  149.              Flags=4: LZ77 compressed, TopicBlockSize 4k
  150.              Flags=8: LZ77 compressed, TopicBlockSize 2k
  151. Additionally the help file may use phrase compression (oldstyle or Hall).
  152.  
  153. If Minor is 16 or less, the help file title follows the SYSTEMHEADER:
  154.  
  155. STRINGZ HelpFileTitle
  156.  
  157. If Minor is above 16, one or more SYSTEMREC records follow instead up to the
  158. internal end of the |SYSTEM file:
  159.  
  160. struct
  161. {
  162.     unsigned short RecordType           type of data in record
  163.     unsigned short DataSize           size of data
  164.     ----
  165.     char Data[DataSize]            dependent on RecordType
  166. }
  167. SYSTEMREC[]
  168.  
  169. There are different RecordTypes defined, each storing different Data.
  170. They mainly contain what was specified in the help project file.
  171.  
  172. RecordType  Data
  173. 1 TITLE     STRINGZ Title           help file title
  174. 2 COPYRIGHT STRINGZ Copyright           copyright notice shown in AboutBox
  175. 3 CONTENTS  TOPICOFFSET Contents       topic offset of starting topic
  176. 4 CONFIG    STRINGZ Macro           all macros executed on opening
  177. 5 ICON        Windows *.ICO file           See WIN31WH on icon file format
  178. 6 WINDOW    struct               Windows defined in the HPJ-file
  179.         {
  180.         struct
  181.         {
  182.             unsigned short TypeIsValid:1
  183.             unsigned short NameIsValid:1
  184.             unsigned short CaptionIsValid:1
  185.             unsigned short XIsValid:1
  186.             unsigned short YIsValid:1
  187.             unsigned short WithIsValid:1
  188.             unsigned short HeigthIsValid:1
  189.             unsigned short MaximizeWindow:1
  190.             unsigned short RGBIsValid:1
  191.             unsigned short RGBNSRIsValid:1
  192.             unsigned short WindowsAlwaysOnTop:1
  193.             unsigned short AutoSizeHeight:1
  194.         }
  195.         Flags
  196.         char Type[10]           type of window
  197.         char Name[9]           window name
  198.         char Caption[51]       caption of window
  199.         short X            x coordinate of window (0..1000)
  200.         short Y            y coordinate of window (0..1000)
  201.         short Width           width of window (0..1000)
  202.         short Height           height of window (0..1000)
  203.         short Maximize           maximize flag and window styles
  204.         COLORREF Rgb           color of scrollable region
  205.         COLORREF RgbNsr        color of non scrollable region
  206.         }
  207.         Window
  208. 6 WINDOW    typedef struct           Viewer 2.0 Windows defined in MVP-file
  209.         {
  210.         unsigned short Flags
  211.         char Type[10]         /* type of window */
  212.         char Name[9]         /* window name */
  213.         char Caption[51]     /* caption for window */
  214.         unsigned char MoreFlags
  215.         short X          /* x coordinate of window (0..1000) */
  216.         short Y          /* y coordinate of window (0..1000) */
  217.         short Width         /* width of window (0..1000) */
  218.         short Height         /* height of window (0..1000) */
  219.         short Maximize         /* maximize flag and window styles */
  220.         COLORREF Rgb1
  221.         char Unknown
  222.         COLORREG Rgb2
  223.         COLORREF Rgb3
  224.         short X2
  225.         short Y2
  226.         short Width2
  227.         short Height2
  228.         short X3
  229.         short Y3
  230.         }
  231.         Window;
  232. 8 CITATION  STRINGZ Citation           the Citation printed
  233. 9 LCID        short LCID[4]           language ID, Windows 95 (HCW 4.00)
  234. 10 CNT        STRINGZ ContentFileName    CNT file name, Windows 95 (HCW 4.00)
  235. 11 CHARSET  unsigned short Charset     charset, Windows 95 (HCW 4.00)
  236. 12 DEFFONT  struct               default dialog font, Windows 95 (HCW 4.00)
  237.         {
  238.         unsigned char HeightInPoints
  239.         unsigned char Charset
  240.         STRINGZ FontName
  241.         }
  242.         DefFont
  243. 12 FTINDEX  STRINGZ dtype           Multimedia Help Files dtypes
  244. 13 GROUPS   STRINGZ Group           defined GROUPs, Multimedia Help File
  245. 14 INDEX_S. STRINGZ IndexSeparators    separators, Windows 95 (HCW 4.00)
  246. 14 KEYINDEX struct               Multimedia Help Files
  247.         {
  248.         char btreename[10];    btreename[1] is footnote character
  249.         char mapname[10];
  250.         char dataname[10];
  251.         char title[80];
  252.         }
  253.         KeyIndex
  254. 18 LANGUAGE STRINGZ language           defined language, Multimedia Help Files
  255. 19 DLLMAPS  struct               defined DLLMAPS, Multimedia Help Files
  256.         {
  257.         STRINGZ Win16RetailDLL
  258.         STRINGZ Win16DebugDLL
  259.         STRINGZ Win32RetailDLL
  260.         STRINGZ Win32DebugDLL
  261.         }
  262.         DLLNames
  263.  
  264. |Phrase
  265.  
  266. If the help file is phrase compressed, it contains an internal file named
  267. |Phrases. Windows 3.0 help files generated with HC30 use the following
  268. uncompressed structure to store phrases. A phrase is not NUL-terminated,
  269. instead use the next PhraseOffset to locate the end of the phrase string
  270. (there is one more phrase offset stored than phrases are defined to allow
  271. for this).
  272.  
  273. unsigned short NumPhrases     number of phrases in table
  274. unsigned short OneHundred     0x0100
  275. unsigned short PhraseOffset[NumPhrases+1] PhraseOffset[0]==2*(NumPhrases+1)
  276. char Phrase[NumPhrases][PhraseOffset[PhraseNum+1]-PhraseOffset[PhraseNum]]
  277.  
  278. Windows 3.1 help files generated using HC31 and later always LZ77 compress
  279. the Phrase character array. Read NumPhrases, OneHundred, DecompressedSize,
  280. and NumPhrases+1 PhraseOffset values. Allocate DecompressedSize bytes for
  281. the Phrase character array and decompress the UsedSpace-2*NumPhrases-10
  282. remaining bytes into the allocated space to retrieve the phrase strings.
  283.  
  284. unsigned short NumPhrases     number of phrases in table
  285. unsigned short OneHundred     0x0100
  286. long DecompressedSize
  287. unsigned short PhraseOffset[NumPhrases+1] PhraseOffset[0]==2*(NumPhrases+1)
  288. ----                 the remaining part is LZ77 compressed
  289. char Phrase[NumPhrases][PhraseOffset[PhraseNum+1]-PhraseOffset[PhraseNum]]
  290.  
  291. The LZ77 decompression algorithm can best be described like this:
  292.   Take the next byte
  293.     Start at the least significant bit
  294.     If the bit is cleared
  295.       Copy 1 byte from source to destination
  296.     Else
  297.       Get the next WORD into the struct { unsigned pos:12; unsigned len:4; }
  298.       Copy len+3 bytes from destination-pos-1 to destination
  299.     Loop until all bits are done
  300.   Loop until all bytes are consumed
  301. See end of this file for a detailed algorithm.
  302.  
  303. Some MVBs use a slightly different layout of internal |Phrases file:
  304.  
  305. unsigned short EightHundred     0x0800
  306. unsigned short NumPhrases     number of phrases in table
  307. unsigned short OneHundred     0x0100
  308. long DecompressedSize
  309. char unused[30]
  310. unsigned short PhraseOffset[NumPhrases+1] PhraseOffset[0]==2*(NumPhrases+1)
  311. ----                 the remaining part is LZ77 compressed
  312. char Phrase[NumPhrases][PhraseOffset[PhraseNum+1]-PhraseOffset[PhraseNum]]
  313.  
  314. |PhrIndex
  315.  
  316. Windows 95 (HCW 4.00) may use Hall compression and the internal files
  317. |PhrIndex and |PhrImage to store phrases. Both must be used to build a
  318. table of phrases and PhraseOffsets. |PhrIndex starts with this header:
  319.  
  320. long Magic             1L
  321. long NEntries
  322. long CompressedSize
  323. long PhrImageSize
  324. long PhrImageCompressedSize
  325. long Always0             0L
  326. unsigned short BitCount:4
  327. unsigned short UnknownBits:12
  328. unsigned short Always4A00     not really always
  329.  
  330. The remaining data is bitcompressed. Use this algorithm to build a table
  331. of PhraseOffsets:
  332.  
  333. short n,i; long mask=0,*ptr=(long *)(&always4A00+1);
  334. int GetBit(void)
  335. {
  336.     ptr+=(mask<0);
  337.     mask=mask*2+(mask<=0);
  338.     return (*ptr&mask)!=0;
  339. }
  340. PhaseOffset[0]=0;
  341. for(i=0;i<NEntries;i++)
  342. {
  343.     for(n=1;GetBit();n+=1<<BitCount) ;
  344.     if(GetBit()) n+=1;
  345.     if(BitCount>1) if(GetBit()) n+=2;
  346.     if(BitCount>2) if(GetBit()) n+=4;
  347.     if(BitCount>3) if(GetBit()) n+=8;
  348.     if(BitCount>4) if(GetBit()) n+=16;
  349.     PhraseOffset[i+1]=PhraseOffset[i]+n;
  350. }
  351.  
  352. Just behind the bitcompressed phrase length information (on a 32-bit boundary,
  353. that's why GetBit consumed longs) follow NumPhrases bits (one bit for each
  354. phrase). It is assumed that this information is used for the full text search
  355. capability to exclude certain phrases.
  356.  
  357. |PhrImage
  358.  
  359. The |PhrImage file stores the phrases. A phrase is not NUL-terminated. Use
  360. PhraseOffset[NumPhrase] and PhraseOffset[NumPhrase+1] to locate beginning
  361. and end of the phrase string. We generated one more PhraseOffset to allow
  362. for this. |PhrImage is LZ77 compressed if PhrImageCompressedSize is not
  363. equal to PhrImageSize. Otherwise you may take it as stored.
  364.  
  365. |FONT
  366.  
  367. The next internal file described is the |FONT file, which uses this header:
  368.  
  369. unsigned short NumFacenames          number of face names
  370. unsigned short NumDescriptors          number of font descriptors
  371. unsigned short FacenamesOffset          start of array of face names
  372.                       relative to &NumFacenames
  373. unsigned short DescriptorsOffset      start of array of font descriptors
  374.                       relative to &NumFacenames
  375. ---                      only if FacenamesOffset >= 12
  376. unsigned short NumStyles          number of style descriptors
  377. unsigned short StyleOffset          start of array of style descriptors
  378.                       relative to &NumFacenames
  379. ---                      only if FacenamesOffset >= 16
  380. unsigned short NumCharMapTables       number of character mapping tables
  381. unsigned short CharMapTableOffset     start of array of character mapping
  382.                       table names relative to &NumFacenames
  383.  
  384. The face name array is located at FacenamesOffset and contains strings, which
  385. are Windows font names or in case of multimedia files a Windows font name
  386. concatenated with ',' and the character mapping table number. Short strings
  387. are NUL-terminated, but a string may use all bytes for characters.
  388.  
  389. char FaceName[NumFacenames][(DescriptorsOffset-FacenamesOffset)/NumFacenames]
  390.  
  391. At DescriptorsOffset is an array located describing all fonts used in the help
  392. file. If this kind of descriptor appears in a help file, any metric value is
  393. given in HalfPoints.
  394.  
  395. struct oldfont
  396. {
  397.     struct
  398.     {
  399.     unsigned char Bold:1
  400.     unsigned char Italic:1
  401.     unsigned char Underline:1
  402.     unsigned char StrikeOut:1
  403.     unsigned char DoubleUnderline:1
  404.     unsigned char SmallCaps:1
  405.     }
  406.     Attributes
  407.     unsigned char HalfPoints              PointSize * 2
  408.     unsigned char FontFamily              font family. See values below
  409.     unsigned short FacenameIndex          index into FaceName array
  410.     unsigned char FGRGB[3]              RGB values of foreground
  411.     unsigned char BGRGB[3]              unused background RGB Values
  412. }
  413. FontDescriptor[NumDescriptors]
  414.  
  415. #define FAM_MODERN 0x01               This is a different order than
  416. #define FAM_ROMAN  0x02               FF_ROMAN, FF_SWISS, etc. of
  417. #define FAM_SWISS  0x03               windows !
  418. #define FAM_TECH   0x03
  419. #define FAM_NIL    0x03
  420. #define FAM_SCRIPT 0x04
  421. #define FAM_DECOR  0x05
  422.  
  423. Multimedia MVB files use different structures to store font descriptors.
  424. Assume this structure for descriptors if FacenamesOffset is at least 12.
  425. If this kind of descriptor is used, any metric is given in twips.
  426.  
  427. struct newfont
  428. {
  429.     unsigned char unknown1
  430.     short FacenameIndex
  431.     unsigned char FGRGB[3]
  432.     unsigned char BGRGB[3]
  433.     unsigned char unknown5
  434.     unsigned char unknown6
  435.     unsigned char unknown7
  436.     unsigned char unknown8
  437.     unsigned char unknown9
  438.     long Height
  439.     unsigned char mostlyzero[12]
  440.     short Weight
  441.     unsigned char unknown10
  442.     unsigned char unknown11
  443.     unsigned char Italic
  444.     unsigned char Underline
  445.     unsigned char StrikeOut
  446.     unsigned char DoubleUnderline
  447.     unsigned char SmallCaps
  448.     unsigned char unknown17
  449.     unsigned char unknown18
  450.     unsigned char PitchAndFamily      Same values as windows LOGFONT
  451. }
  452. FontDescriptor[NumDescriptors]
  453.  
  454. Assume this structure for descriptors if FacenamesOffset is at least 16.
  455. If this kind of descriptor is used, any metric is given in twips.
  456.  
  457. struct mvbfont
  458. {
  459.     short FacenameIndex           index into Facename array
  460.     short StyleNumber              0 if not used
  461.     unsigned char unknown3
  462.     unsigned char unknown4
  463.     unsigned char FGRGB[3]
  464.     unsigned char BGRGB[3]
  465.     long Height               negative (incl. external leading)
  466.     unsigned char mostlyzero[12]
  467.     short Weight
  468.     unsigned char unknown10
  469.     unsigned char unknown11
  470.     unsigned char Italic
  471.     unsigned char Underline
  472.     unsigned char StrikeOut
  473.     unsigned char DoubleUnderline
  474.     unsigned char SmallCaps
  475.     unsigned char unknown17
  476.     unsigned char unknown18
  477.     unsigned char PitchAndFamily      Same values as windows LOGFONT
  478.     unsigned char unknown20
  479.     unsigned char unknown21
  480. }
  481. FontDescriptor[NumDescriptors]
  482.  
  483. If FacenamesOffset is at least 12, the |FONT file supports character styles.
  484. StyleNumber-1 of the FontDescriptor indexes into this array located at
  485. StyleOffset in |FONT.
  486.  
  487. struct
  488. {
  489.     short StyleNum
  490.     short BasedOnStyleNum           0 if not used
  491.     struct Font                struct newfont or struct mvbfont
  492.     char unknown[35]
  493.     char StyleName[65]
  494. }
  495. Style[NumStyles]
  496.  
  497. If FacenamesOffset is at least 16, the |FONT file supports character mapping
  498. tables.
  499.  
  500. The array of character mapping table file names is located in |FONT at
  501. CharMapTableOffset and contains strings of the internal filename of the
  502. character mapping table concatenated with ',' and the character mapping table
  503. number. The entries are not sorted by character mapping table numbers. Short
  504. strings are NUL-terminated, but a string may use up all bytes.
  505.  
  506. char CharMapTableName[NumCharMapTables][32]
  507.  
  508. |TOMAP
  509.  
  510. Windows 3.0 (HC30) uses topic numbers that start at 16 for the first topic
  511. to identify topics. To retrieve the location of the TOPICLINK for the TOPIC-
  512. HEADER of a certain topic (in |TOPIC explained later), use the |TOMAP file.
  513. It contains an array of topic positions. Index with TopicNumber (do not
  514. subtract 16). TopicPos[0] points to the topic specified as INDEX in the help
  515. project.
  516.  
  517. TOPICPOS TopicPos[UsedSpace/4]
  518.  
  519. |CONTEXT
  520.  
  521. Windows 3.1 (HC31) uses hash values of context names to identify topics.
  522. To get the location of the topic, search the B+ tree of the internal file
  523. |CONTEXT:
  524.  
  525. Structure of |CONTEXT index-page entries:
  526. struct
  527. {
  528.     long HashValue
  529.     short PageNumber
  530. }
  531. CONTEXTINDEXENTRY[NEntries]
  532.  
  533. Structure of |CONTEXT leaf-page entries:
  534. struct
  535. {
  536.     long HashValue           hash value of context id
  537.     TOPICOFFSET TopicOffset    position
  538. }
  539. CONTEXTLEAFENTRY[NEntries]
  540.  
  541. To calculate the HashValue hash from a context id ptr do this:
  542.  
  543. signed char table[256]=
  544. {
  545.     '\x00', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7',
  546.     '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF',
  547.     '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7',
  548.     '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF',
  549.     '\xF0', '\x0B', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7',
  550.     '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\x0C', '\xFF',
  551.     '\x0A', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
  552.     '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F',
  553.     '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
  554.     '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F',
  555.     '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
  556.     '\x28', '\x29', '\x2A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0D',
  557.     '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
  558.     '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F',
  559.     '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
  560.     '\x28', '\x29', '\x2A', '\x2B', '\x2C', '\x2D', '\x2E', '\x2F',
  561.     '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
  562.     '\x58', '\x59', '\x5A', '\x5B', '\x5C', '\x5D', '\x5E', '\x5F',
  563.     '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
  564.     '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F',
  565.     '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
  566.     '\x78', '\x79', '\x7A', '\x7B', '\x7C', '\x7D', '\x7E', '\x7F',
  567.     '\x80', '\x81', '\x82', '\x83', '\x0B', '\x85', '\x86', '\x87',
  568.     '\x88', '\x89', '\x8A', '\x8B', '\x8C', '\x8D', '\x8E', '\x8F',
  569.     '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
  570.     '\x98', '\x99', '\x9A', '\x9B', '\x9C', '\x9D', '\x9E', '\x9F',
  571.     '\xA0', '\xA1', '\xA2', '\xA3', '\xA4', '\xA5', '\xA6', '\xA7',
  572.     '\xA8', '\xA9', '\xAA', '\xAB', '\xAC', '\xAD', '\xAE', '\xAF',
  573.     '\xB0', '\xB1', '\xB2', '\xB3', '\xB4', '\xB5', '\xB6', '\xB7',
  574.     '\xB8', '\xB9', '\xBA', '\xBB', '\xBC', '\xBD', '\xBE', '\xBF',
  575.     '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7',
  576.     '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF'
  577. }
  578. for(hash=0L;*ptr;ptr++) hash=(hash*43)+table[(unsigned char)*ptr];
  579.  
  580. Remember that only 0-9, A-Z, a-z, _ and . are legal characters for context ids
  581. in Win 3.1 (HC31). Only Windows 95 (HCRTF) allows nearly all characters.
  582. The hash value for an empty string is 1.
  583.  
  584. |CTXOMAP
  585.  
  586. If your help project file had a [MAP] section, the internal file |CTXOMAP
  587. contains an array to assign map ids to topic offsets.
  588.  
  589. short NEntries
  590. struct
  591. {
  592.     long MapID
  593.     TOPICOFFSET TopicOffset
  594. }
  595. CTXOMAPENRTY[NEntries]
  596.  
  597. |xWBTREE, |xWDATA, |xWMAP, |xKWBTREE, |xKWDATA, |xKWMAP
  598.  
  599. To locate a keyword assigned using a x-footnote (x may be A-Z, a-z), use the
  600. |xWDATA, |xWBTREE and |xWMAP internal files. |xWBTREE tells you how often a
  601. certain Keyword is defined in the help file.
  602.  
  603. Structure of |xWBTREE index page entries:
  604. struct
  605. {
  606.     STRINGZ Keyword
  607.     short PageNumber
  608. }
  609. xWBTREEINDEXENTRY[NEntries]
  610.  
  611. Structure of |xWBTREE leaf page entries:
  612. struct
  613. {
  614.     STRINGZ Keyword
  615.     short Count         number of times keyword is referenced
  616.     long KWDataOffset        this is the offset into |xWDATA
  617. }
  618. xWBTREELEAFENTRY[NEntries]
  619.  
  620. KWBTREE files in WinHlp32 GID files are structured differently (they have
  621. a different description in the structure field of the BTREEHEADER) and pack
  622. former KWBTREE and KWDATA files into one:
  623.  
  624. Structure of |xWBTREE leaf page entries in Win95 GID files:
  625.  
  626. struct
  627. {
  628.     STRINGZ Keyword
  629.     long Size            size of following record
  630.     struct
  631.     {
  632.     long FileNumber     ?
  633.     long TopicOffset    this is the offset into |xWDATA
  634.     }
  635.     record[Size/8]
  636. }
  637. xWBTREELEAFENTRY[NEntries]
  638.  
  639. The |xWDATA contains an array of topic offsets. The KWDataOffset from the
  640. |xWBTREE tells you where to seek to in the |xWDATA file to read Count topic
  641. offsets.
  642.  
  643. TOPICOFFSET KeywordTopicOffset[UsedSpace/4]
  644.  
  645. And the topic offset retrieved tells you which location the Keyword was
  646. assigned to. It is -1L if the Keyword is assigned to a macro using the [MACROS]
  647. section of HCRTF 4.0 (see description of |Rose file).
  648.  
  649. The |xWMAP contains an array that tells you where to find the n-th keyword in
  650. the |xWBTREE. You don't need to use this file but it allows for faster
  651. scrolling lists of alphabetically ordered Keywords. (WinHelp search dialog).
  652.  
  653. struct
  654. {
  655.     long KeywordNumber          number of first keyword on leaf-page
  656.     unsigned short PageNum    B+ tree page number
  657. }
  658. xWMAP[UsedSpace/6]
  659.  
  660. Similarily |xKWBTREE B+ tree and |xKWDATA, |xKWMAP files (where x may be 0-9,
  661. A-Z, a-z) are built from K-x:footnotes and [KEYINDEX] declarations of multi
  662. media files.
  663.  
  664. |TTLBTREE
  665.  
  666. If you want to know the topic title assigned using the $-footnote, take a look
  667. into the |TTLBTREE internal file, which contains topic titles ordered by topic
  668. offsets in a B+ tree. (It is used by WinHelp to display the topic titles in
  669. the search dialog).
  670.  
  671. Structure of |TTLBTREE index page entries:
  672. struct
  673. {
  674.     TOPICOFFSET TopicOffset
  675.     short PageNumber
  676. }
  677. TTLBTREEINDEXENTRY[NEntries]
  678.  
  679. Structure of |TTLBTREE leaf page entries:
  680. struct
  681. {
  682.     TOPICOFFSET TopicOffset
  683.     STRINGZ TopicTitle
  684. }
  685. TTLBTREELEAFENTRY[NEntries]
  686.  
  687. |CFn
  688.  
  689. The |CFn (where n is integer) internal file lists the macros defined in
  690. [CONFIG:n] sections of the help project file (HCW 4.00). The file contains as
  691. many macro strings as were specified one after another:
  692.  
  693. STRINGZ Macro[]
  694.  
  695. |Rose
  696.  
  697. The |Rose internal file contains all definitions from the [MACROS] section of a
  698. Windows 95 (HCW 4.00) help project file. It is build using a B+ tree. Keywords
  699. only appear using hash values but are listed in the |KWBTREE with a TopicPos in
  700. the associated |KWDATA array of -1L.
  701.  
  702. Structure of |Rose index page entries:
  703. struct
  704. {
  705.     long KeywordHash
  706.     short PageNumber
  707. }
  708. RoseINDEXENTRY[NEntries]
  709.  
  710. Structure of |Rose leaf page entries:
  711. struct
  712. {
  713.     long KeywordHash
  714.     STRINGZ Macro
  715.     STRINGZ TopicTitle         not a real topic title but the string
  716.                  displayed in the search dialog where
  717.                  normally topic titles are listed
  718. }
  719. RoseLEAFENTRY[NEntries]
  720.  
  721. |TopicId
  722.  
  723. The |TopicId internal file lists the ContextName assigned to a specific topic
  724. offset if the help file was created using the /a option of HCRTF and is build
  725. using a B+ tree.
  726.  
  727. Structure of |TopicId index-page entries:
  728. struct
  729. {
  730.     TOPICOFFSET TopicOffset
  731.     short PageNumber
  732. }
  733. TopicIdINDEXENTRY[NEntries]
  734.  
  735. Structure of |TopicId leaf-page entries:
  736. struct
  737. {
  738.     TOPICOFFSET TopicOffset
  739.     STRINGZ ContextName
  740. }
  741. TopicIdLEAFENTRY[NEntries]
  742.  
  743. |Petra
  744.  
  745. The |Petra internal file contains a B+ tree mentioning the names of the RTF
  746. source files the help file was build from for each topic if the help file was
  747. created using the /a option of HCRTF.
  748.  
  749. Structure of |Petra index-page entries:
  750. struct
  751. {
  752.     TOPICOFFSET TopicOffset
  753.     short PageNumber
  754. }
  755. PetraINDEXENTRY[NEntries]
  756.  
  757. Structure of |Petra leaf-page entries:
  758. struct
  759. {
  760.     TOPICOFFSET TopicOffset
  761.     STRINGZ RTFSourceFileName
  762. }
  763. PetraLEAFENTRY[NEntries]
  764.  
  765. |Viola
  766.  
  767. The |Viola internal file contains a B+ tree specifying the default Windows
  768. assigned to topics using the > footnote available in HCRTF 4.00.
  769.  
  770. Structure of |VIOLA index-page entries:
  771. struct
  772. {
  773.     TOPICOFFSET TopicOffset
  774.     short PageNumber
  775. }
  776. VIOLAINDEXENTRY[NEntries]
  777.  
  778. Structure of |VIOLA leaf-page entries:
  779. struct
  780. {
  781.     TOPICOFFSET TopicOffset
  782.     long DefaultWindowNumber
  783. }
  784. VIOLALEAFENTRY[NEntries]
  785.  
  786. *.GID
  787. I have not investigated GID files, as they are created by WinHlp32 and are not
  788. needed for help file reconstruction. But they are based on the same file format
  789. as Windows help files, so HELPDECO may be used to display their content. Notice
  790. the difference between |xWBTREE files stored in *.GID files and regular files.
  791.  
  792. |WinPos
  793. This file has been seen in WinHlp32 GID files, but always contained an empty
  794. Btree (with an unknown 'a' in the BTREEHEADER structure).
  795.  
  796. |Pete
  797. This file has been seen in WinHlp32 GID files but is currently not understood.
  798.  
  799. |Flags
  800. This file has been seen in WinHlp32 GID files but is currently not understood.
  801.  
  802. |CntJump
  803. This B+ tree stored in WinHlp32 GID files contains the jump references of
  804. the *.CNT file.
  805.  
  806. |CntText
  807. This B+ tree stored in WinHlp32 GID files contains the topic titles of the
  808. jumps from the *.CNT file.
  809.  
  810. *.GRP
  811. MediaView compilers create *.GRP internal files from group + footnotes
  812. assigned to topics. All *.GRP files follow this structure:
  813.  
  814. struct
  815. {
  816.     unsigned long Magic      /* 0x000A3333 */
  817.     unsigned long BitmapSize /* max. 64000 equalling 512000 topics */
  818.     unsigned long LastTopic  /* first topic in help file has topic number 0 */
  819.     unsigned long FirstTopic /* first topic in help file has topic number 0 */
  820.     unsigned long TopicsUsed /* in this group */
  821.     unsigned long TopicCount /* in whole help file */
  822.     unsigned long GroupType  /* 1 or 2, see below */
  823.     unsigned long Unknown[3]
  824.     unsigned char Bitmap[BitmapSize] /* only if GroupType equals 2 */
  825. }
  826. GROUP
  827.  
  828. Starting with the first topic of the help file using TopicNumber 0, a topic is
  829. included in a group if TopicNumber is in the range of FirstTopic to LastTopic.
  830. If GroupType equals 2 it is additionally required that the corresponding bit
  831. starting with lsb of Bitmap[0] is set in the Bitmap.
  832. (Bitmap[TopicNumber>>3]&(1<<(TopicNumber&7))!=0).
  833.  
  834. *.tbl
  835.  
  836. MediaView compilers store character mapping tables listed in the [CHARTAB]
  837. section in internal *.tbl files using the following binary structure:
  838.  
  839. struct
  840. {
  841.     unsigned short Magic /* 0x5555 */
  842.     unsigned short Size
  843.     unsigned short Unknown1[2]
  844.     unsigned short Entries
  845.     unsigned short Ligatures
  846.     unsigned short LigLen
  847.     unsigned short Unknown2[13]
  848.     struct
  849.     {
  850.     unsigned short class
  851.     unsigned short order
  852.     unsigned char normal
  853.     unsigned char clipboard
  854.     unsigned char mac
  855.     unsigned char macclipboard
  856.     unsigned short unused
  857.     }
  858.     charentry[Entries]
  859.     unsigned char Ligature[Ligatures][LigLen]
  860. }
  861. CHARTAB
  862.  
  863. A character mapping table is assigned to a font by appending ,x (where x is a
  864. decimal number) to the font name and the same ,x to the character mapping table
  865. name (in the CHARMAP section of the internal |FONT file).
  866.  
  867. |TOPIC
  868.  
  869. And now to the interesting part, the internal file named |TOPIC. It's divided
  870. into blocks of TopicBlockSize bytes, each beginning with a TOPICBLOCKHEADER:
  871.  
  872. TOPICPOS LastTopicLink      points to last topic link in previous block or -1L
  873. TOPICPOS FirstTopicLink   points to first topic link in this block
  874. TOPICPOS LastTopicHeader  points to topic link of last topic header or 0L, -1L
  875. ----
  876. char PlainOrCompressedData[TopicBlockSize-12]
  877.  
  878. Read the first 12 bytes into a TOPICBLOCKHEADER structure. The remaining
  879. TopicBlockSize-12 bytes of each topic block may be compressed using the LZ77
  880. algorithm described above.
  881. Decompress them into a buffer of DecompressSize bytes size if the Flags value
  882. contained in the internal |SYSTEM file is 4 or 8 and Minor is greater than 16
  883. (DecompressSize is 16k this way), else they are not compressed and you should
  884. copy them as delivered (DecompressSize=TopicBlockSize-12).
  885. Do not decompress to more than DecompressSize bytes. As this would cause
  886. ambiguos values for TOPICPOS, the help compilers will not compress more, but
  887. fill the remaining topic block with 0es. Data will continue in the next
  888. topic block.
  889.  
  890. TOPICPOS
  891.  
  892. A TOPICPOS is used to locate the position of TOPICLINKs in |TOPIC and contains
  893. the TopicBlockNumber in it's higher bits and an offset into the decompression
  894. buffer in it's lower bits.
  895. How many bits are used for TopicBlockNumber and TopicBlockOffset depends on
  896. the compression method used and the TopicBlockSize:
  897.  
  898. (TOPICPOS-sizeof(TOPICBLOCKHEADER))%DecompressSize = TopicBlockOffset
  899. (TOPICPOS-sizeof(TOPICBLOCKHEADER))/DecompressSize = TopicBlockNumber
  900.  
  901. A TOPICPOS below sizeof(TOPICBLOCKHEADER) is invalid.
  902.  
  903. TOPICLINK
  904.  
  905. A TOPICLINK (located inside the buffer after decompression, the first of it
  906. pointed to by TOPICBLOCKHEADERs FirstTopicLink field) looks like this:
  907.  
  908. long BlockSize          Size of TOPICLINK + LinkData1 + compressed LinkData2
  909. long DataLen2          length of decompressed LinkData2
  910. TOPICPOS PrevBlock      Windows 3.0 (HC30): Number of bytes previous
  911.               TOPICLINK is located before this TOPICLINC,
  912.               including eventually skipped TOPICBLOCKHEADER and
  913.               unused bytes.
  914.               Windows 3.1 (HC31): TOPICPOS of previous TOPICLINK
  915. TOPICPOS NextBlock      Windows 3.0 (HC30): Number of bytes next TOPICLINK
  916.               is located behind this TOPICLINK, incl. eventually
  917.               skipped TOPICBLOCKHEADER and unused bytes.
  918.               Windows 3.1 (HC31): TOPICPOS of next TOPICLINK
  919. long DataLen1          includes size of TOPICLINK
  920. unsigned char RecordType  See below
  921. ----
  922. char LinkData1[DataLen1-11]
  923. char LinkData2[BlockSize-DataLen1]
  924.  
  925. LinkData2 may be compressed using Phrase compression. If you find
  926. DataLen2>BlockSize-DataLen1 use the following algorithm to decompress
  927. if your help file contains a |Phrases internal file:
  928.  
  929.   Take the next character. If it's value is 0 or above 15 emit it. Else
  930.   multiply it with 256, subtract 256 and add the value of the next character.
  931.   Divide by 2 to get the phrase number. Emit the phrase from the |Phrase file
  932.   and append a space if the division had a remainder (the number was odd).
  933.  
  934. If the help file doesn't contain a |Phrases file but instead a |PhrIndex
  935. and |PhrImage, it uses Hall compression and the decompression of LinkData2
  936. is a bit more difficult:
  937.  
  938.   Take the next character (ch). If ch is even emit the phrase number ch/2.
  939.   Else if the least two bits are 01 multiply by 64, add 64 and the value of
  940.   the next character. Emit the Phrase using this number. If the least three
  941.   bits are 011 copy the next ch/8+1 characters. If the least four bits are
  942.   0111 emit ch/16+1 spaces. If the least four bits are 1111 emit ch/16+1 NUL's.
  943.  
  944. If DataLen2<=BlockSize-DataLen1 the DataLen2 bytes of LinkData2 are stored
  945. uncompressed (makes a difference for Hall compression only).
  946. If DataLen2<BlockSize-DataLen1 the remaining BlockSize-DataLen1-DataLen2 bytes
  947. are unused, but must be read from the |TOPIC file (this can only happen in Hall
  948. compressed help files).
  949.  
  950. Now that you know how to decompress the topic data, let's see what you get.
  951. If the TOPICLINK RecordType is 2 you got a topic header in LinkData1.
  952. In Windows 3.0 (HC30) the TOPICHEADER is structured like this:
  953.  
  954. long BlockSize          size of topic, including internal topic links
  955. long PrevTopicNumber      -1L or 0xFFFF at the beginning of a browse sequence
  956. long NextTopicNumber      -1L or 0xFFFF at the end of a browse sequence
  957.  
  958. In Windows Version 3.1 (HC31) and later it looks like this:
  959.  
  960. long BlockSize          size of topic, including internal topic links
  961. TOPICOFFSET BrowseBck      topic offset for prev topic in browse sequence
  962. TOPICOFFSET BrowseFor      topic offset for next topic in browse sequence
  963. long TopicNum          topic number
  964. TOPICPOS NonScroll      start of non-scrolling region (topic offset) or -1L
  965. TOPICPOS Scroll       start of scrolling region (topic offset)
  966. TOPICPOS NextTopic      start of next type 2 record
  967.  
  968. The LinkData2 of Topic RecordType 2 contains NUL terminated strings. The
  969. first string is the topic title, the next strings contain all macros to be
  970. executed on opening this topic (specified using the ! footnote).
  971.  
  972. If the TOPICLINK RecordType is 1, you have a Windows 3.0 displayable text
  973. record, a RecordType of 0x20 is Windows 3.1 displayable text and 0x23 is
  974. a Windows 3.1 table record. A displayable text record may contain multiple
  975. paragraphs, but all have the same paragraph formatting. A table record
  976. stores all rows and columns of a table and may contain multiple paragraphs
  977. of different formatting.
  978.  
  979. Data inside LinkData1 is sometimes stored as compressed shorts or longs:
  980.   A compressed unsigned short is made of a single byte. Divide by two to get
  981.   the value if it's even. Divide by two and add 128 times the value of the
  982.   next byte if it's odd.
  983.   A compressed signed short is made of a single byte. Divide by two and sub-
  984.   tract 64 to get the value if it's even. Divide by two, add 128 times the
  985.   value of the next byte and subtract 16384 if it's odd.
  986.   A compressed unsigned long is made of a 2 byte value. Divide by two to get
  987.   it's value if it's even. Divide by two and add 32768 times the value of the
  988.   next 2 bytes if it's odd.
  989.   A compressed signed long is made of a 2 byte value. Divide by two and sub-
  990.   tract 16384 to get it's value if it's even. Divide by two, add 32768 times
  991.   the value of the next 2 bytes and subtract 67108864 if it's odd.
  992.  
  993. The structure of LinkData1 in RecordType 1, 0x20, and 0x23 is difficult to
  994. describe, as some values are only stored if a certain condition is met and
  995. is therefore of variable size. I try to describe them as a C-structure and
  996. note which fields are not present under certain circumstances. Don't
  997. declare this structure. Write a parser which reads a value only if it's
  998. condition is met.
  999.  
  1000. The metric used (GapWidth, LeftIndent, etc.) is dependend upon the Font-
  1001. Descriptor used (See |FONT file). It may be HalfPoints or Twips.
  1002.  
  1003. compressed long TopicSize
  1004. struct                    only in records type 0x20 and 0x23
  1005. {
  1006.     compressed unsigned short TopicLength
  1007.     struct                only in records type 0x23
  1008.     {
  1009.     unsigned char NumberOfColumns
  1010.     unsigned char TableType     0,2=variable width, 1,3=normal
  1011.     struct                only for TableType 0 and 2
  1012.     {
  1013.         short MinTableWidth
  1014.     }
  1015.     ForTableType0or2only
  1016.     struct
  1017.     {
  1018.         short GapWidth        LeftMargin if first column
  1019.         short ColWidth        relative in variable width tables
  1020.                     Sum of all GapWidth/ColWidth values
  1021.                     is 32767 in variable width tables
  1022.     }
  1023.     Column[NumberOfColumns]
  1024.     }
  1025.     RecordType0x23only
  1026. }
  1027. RecordType0x20or0x23only
  1028. struct
  1029. {
  1030.     struct                only in RecordType 0x23
  1031.     {
  1032.     short column            -1 if end of topic, don't continue
  1033.     short unknown
  1034.     char always0
  1035.     }
  1036.     RecordType0x23only
  1037.     unsigned char unknownUnsignedChar
  1038.     char unknownBiasedChar
  1039.     unsigned short id
  1040.     struct
  1041.     {
  1042.     unsigned short UnknownFollows:1
  1043.     unsigned short SpacingAboveFollows:1
  1044.     unsigned short SpacingBelowFollows:1
  1045.     unsigned short SpacingLinesFollows:1
  1046.     unsigned short LeftIndentFollows:1
  1047.     unsigned short RightIndentFollows:1
  1048.     unsigned short FirstlineIndentFollows:1
  1049.     unsigned short unused:1
  1050.     unsigned short BorderinfoFollows:1
  1051.     unsigned short TabinfoFollows:1
  1052.     unsigned short RightAlignedParagraph:1
  1053.     unsigned short CenterAlignedParagraph:1
  1054.     }
  1055.     bits
  1056.     compressed long  Unknown        only if UnknownFollows set
  1057.     compressed short SpacingAbove    only if SpacingAboveFollows set
  1058.     compressed short SpacingBelow    only if SpacingBelowFollows set
  1059.     compressed short SpacingLines    only if SpacingLinesFollows set
  1060.     compressed short LeftIndent     only if LeftIndentFollows set
  1061.     compressed short RightIndent    only if RightIndentFollows set
  1062.     compressed short FirstlineIndent    only if FirstlineIndentFollows set
  1063.     struct                only if BorderinfoFollows set
  1064.     {
  1065.     unsigned char BorderBox:1
  1066.     unsigned char BorderTop:1
  1067.     unsigned char BorderLeft:1
  1068.     unsigned char BorderBottom:1
  1069.     unsigned char BorderRight:1
  1070.     unsigned char BorderThick:1
  1071.     unsigned char BorderDouble:1
  1072.     unsigned char BorderUnknown:1
  1073.     short BorderWidth
  1074.     }
  1075.     Borderinfo
  1076.     struct                only if TabinfoFollows set
  1077.     {
  1078.     compressed short NumberOfTabStops
  1079.     struct
  1080.     {
  1081.         compressed unsigned short TabStop  position is lower 14 bits
  1082.         struct                   only if TabStop bit 0x4000 set
  1083.         {
  1084.         compressed unsigned short TabType      1=right, 2=center
  1085.         }
  1086.         onlyIfTabStopBit0x4000set
  1087.     }
  1088.     Tab[NumberOfTabStops]
  1089.     }
  1090.     Tabinfo
  1091. }
  1092. Paragraphinfo
  1093.  
  1094. Behind this structure LinkData1 contains character formatting information.
  1095. Always output the next string (NUL terminated) from LinkData2 (use Phrase
  1096. decompression if required), than read the next formatting command, set up
  1097. the required font, color or position before displaying the next string.
  1098. Sometimes the string is of zero length, as multiple formatting commands are
  1099. required before output.
  1100.  
  1101. 0xFF: end of character formatting. Proceed with next Paragraphinfo if
  1102.       RecordType is 0x23, else you are done.
  1103.  
  1104. 0x20: long vfldNumber      0 = {vfld}   n = {vfld n}
  1105.  
  1106. 0x21: short dtypeNumber   0 = {dtype}  n = {dtype n}
  1107.  
  1108. 0x80: short FontNumber      index into Descriptor array of internal |FONT file
  1109.  
  1110. 0x81: line break      no firstlineindent/spacingabove on next paragraph
  1111.  
  1112. 0x82: end of paragraph      next paragraph has same Paragraphinfo as this one
  1113.  
  1114. 0x83: TAB          jump to next tab stop
  1115.  
  1116. 0x86: ewc or bmc or bmcwd or bmct or button or mci
  1117. 0x87: ewl or bml or bmlwd or bmlt or button or mci_left
  1118. 0x88: ewr or bmr or bmrwd or bmrt or button or mci_right
  1119.       unsigned char Type        5=embedded, 3 or 0x22=picture
  1120.       compressed long PictureSize    size of union
  1121.       struct                only if Type = 0x22
  1122.       {
  1123.       compressed word NumberOfHotspots    Add to TopicPos if counting
  1124.       }
  1125.       OnlyIfTypeIs0x22
  1126.       union
  1127.       {
  1128.       struct
  1129.       {
  1130.           short PictureIsEmbedded    0=bmc/bmr/bml or 1=bmcwd/bmlwd/bmrwd
  1131.           short PictureNumber    only if PictureIsEmbedded = 0
  1132.           char EmbeddedPicture[PictureSize-4]
  1133.                     only if PictureIsEmbedded = 1
  1134.                     See 'Format of Pictures' section
  1135.       }
  1136.       Type3or0x22
  1137.       struct
  1138.       {
  1139.           short unknown1
  1140.           short unknown2
  1141.           short unknown3
  1142.           STRINGZ Embedded        Format of string depends on statement
  1143.               DLLName,WindowClass,Param     if ewc/ewr/ewl
  1144.               !Label,Macro            if button
  1145.               *n,m,[helpfilename+]filename  if mci/mci_left/mci_right
  1146.               n=0x8400
  1147.               n+=2 if NOPLAYBAR specified
  1148.               n+=8 if NOMENU specified
  1149.               m=0
  1150.               m+=1 if PLAY specified
  1151.               n+=2 if REPEAT specified
  1152.               [helpfilename+] if not EXTERNAL
  1153.       }
  1154.       Type5only
  1155.       }
  1156.       PictureData            size of union is PictureSize
  1157.  
  1158. 0x89: end of hotspot      switch back from underlined green
  1159.  
  1160. 0x8B: non-break-space      the blank does not appear in LinkData2
  1161.  
  1162. 0x8C: non-break-hyphen      the hyphen itself is stored in LinkData2
  1163.  
  1164. 0xC8: macro          start with underlined green
  1165. 0xCC: macro without font change
  1166.       short Length
  1167.       char MacroString[Length-3]
  1168.  
  1169. 0xE0: popup jump      start with underlined green
  1170. 0xE1: topic jump      start with underlined green
  1171.       TOPICOFFSET TopicOffset
  1172.  
  1173. 0xE2: popup jump      start with underlined green
  1174. 0xE3: topic jump      start with underlined green
  1175. 0xE3: topic jump      start with underlined green
  1176. 0xE6: popup jump without font change
  1177. 0xE7: topic jump without font change
  1178.       TOPICOFFSET TopicOffset
  1179.  
  1180. 0xEA: popup jump into external file               start with underlined green
  1181. 0xEB: popup jump into external file without font change
  1182. 0xEE: topic jump into external file / secondary window       start with underlined green
  1183. 0xEF: topic jump into external file / secondary window without font change
  1184.       short SizeOfFollowingStruct
  1185.       struct
  1186.       {
  1187.       unsigned char Type        0, 1, 4 or 6
  1188.       TOPICOFFSET TopicOffset
  1189.       unsigned char WindowNumber    only if Type = 1
  1190.       STRINGZ NameOfExternalFile    only if Type = 4 or 6
  1191.       STRINGZ WindowName        only if Type = 6
  1192.       }
  1193.  
  1194. Continue outputting strings from LinkData2 and parsing formatting commands
  1195. from LinkData1 until the 'end of character formatting' command is found.
  1196.  
  1197. TOPICOFFSET
  1198.  
  1199. A TOPICOFFSET is used since WinHelp 3.1 to locate a cursor-like position, even
  1200. in the middle of a topic. The position must be unique for hotspots (tabbing).
  1201. And it needs to be unique for every scrollable position (going 'Back' to a
  1202. topic that was scrolled). And it needs to quickly give you the topic block
  1203. to read from the help file.
  1204.  
  1205. Like a TOPICPOS, a TOPICOFFSET is divided into a TopicBlockNumber in it's
  1206. 17 higher bits (TOPICPOS/32768) and a CharacterCount in it's 15 lower bits
  1207. (TOPICPOS%32768) counting all characters and the number of hotspots in
  1208. pictures appearing in all TOPICLINKs in the topic block before this position.
  1209. If you got a TopicOffset, seek to the TopicBlock in |TOPIC as told by the
  1210. TopicBlockNumber, read in and decompress the whole block. Use FirstTopicLink
  1211. to locate the first TOPICLINK in this decompressed block (CharacterCount is
  1212. 0 at this place) and follow the list of TOPICLINKs up to the desired
  1213. position, adding TopicLength of every RecordType 0x20 and 0x23 you come
  1214. across, until adding TopicLength would exceed the desired CharacterPosition.
  1215. Your position is located in this TL_DISPLAY or TL_TABLE TOPICLINK. Expand
  1216. LinkData2 if phrase compressed and follow the formatting procedure described
  1217. above incrementing CharacterCount on every character (and NUL-terminator)
  1218. passed. Add the NumberOfHotspots if a picture is included.
  1219. If a TOPICLINK crosses a topic block, this has no effect on the TopicBlock-
  1220. Number for this TOPICLINK (i.e. a TOPICOFFSET pointing into the second part
  1221. has the TopicBlockNumber of the beginning of the TOPICLINK).
  1222. If you didn't come across a TOPICHEADER (TOPICLINK RecordType 2) in this
  1223. process, the beginning of the topic is located in a previous block. The
  1224. LastTopicHeader field of the TOPICBLOCKHEADER of the current block tells
  1225. you where to find it.
  1226.  
  1227. WALKING TOPICS
  1228.  
  1229. To follow all topics contained in the help file, set the current TOPICPOS
  1230. to 12 (that's FirstTopicLink of the first TOPICBLOCKHEADER at offset 0 in
  1231. |TOPIC) and load it's TopicBlock ((12-12)/DecompressSize = 0) and decompress.
  1232. The TOPICLINK is located at TopicBlockOffset ((12-12)%DecompressSize = 0)
  1233. in the decompression buffer. The first TOPICLINK contains the TOPICHEADER
  1234. of the first topic.
  1235. In Windows 3.0 (HC30) help files you move from one TOPICLINK to the next
  1236. by adding NextBlock to the current TOPICPOS. If the next TOPICLINK is
  1237. located in the next topic block, the value of NextBlock handles the jump
  1238. over the intervening TOPICBLOCKHEADER and possibly unused bytes nicely.
  1239. In Windows 3.1 (HC31) and later you move from one TOPICLINK to the next
  1240. by setting the current position to NextBlock, which also handles the jump
  1241. from one topic block to the other nicely.
  1242. The last TOPICLINK has NextBlock set to 0 or -1L. The last TOPICLINK does
  1243. not contain any usable data.
  1244.  
  1245. Format of Pictures
  1246.  
  1247. Inside help files Bitmaps and Metafiles are stored in lP- or lp-format. This
  1248. is the format of SHG/MRB files that SHED/MRBC produce and may contain multiple
  1249. pictures at different resolutions, each with optional additional hotspot data.
  1250. Pictures may be embedded in LinkData2 of |TOPIC or appear as |bm<x> files
  1251. (or bm<x> in case of Windows 3.0 HC30). Each picture starts with this header
  1252. data. The PictureOffset tells you where to look for the desired picture.
  1253.  
  1254. short Magic                  0x506C (SHG,lP) or 0x706C (MRB,lp)
  1255. short NumberOfPictures              >1 if multi-resolution-bitmap
  1256. long PictureOffset[NumberOfPictures]      relative to &Magic
  1257.  
  1258. You shouldn't depend on Magic lP/lp upon reading, as there are some MRBs
  1259. flagged like SHG, but please write correct values.
  1260.  
  1261. Seek to PictureOffset and you will find this:
  1262.  
  1263. char PictureType       5=DDB 6=DIB 8=metafile
  1264. char PackingMethod       0=uncompressed 1=RunLen 2=LZ77 3=both
  1265.  
  1266. If PictureType is 5 or 6 the picture is a bitmap described by:
  1267.  
  1268. compressed unsigned long Xdpi            resolution in dpi, not PelsPerMeter
  1269. compressed unsigned long Ydpi            resolution in dpi, not PelsPerMeter
  1270. compressed unsigned short Planes
  1271. compressed unsigned short BitCount
  1272. compressed unsigned long Width
  1273. compressed unsigned long Height
  1274. compressed unsigned long ColorsUsed
  1275. compressed unsigned long ColorsImportant    1 if bitmap is transparent
  1276. compressed unsigned long CompressedSize
  1277. compressed unsigned long HotspotSize        0 if none are defined
  1278. unsigned long CompressedOffset            relative to &PictureType
  1279. unsigned long HotspotOffset            relative to &PictureType
  1280.  
  1281. If PictureType is 6 a color palette follows immediatly
  1282.  
  1283. COLORREF palette[ColorsUsed]            or 1<<BitCount if ColorsUsed=0
  1284.  
  1285. If PackingMethod is 0 copy CompressedSize bytes starting at CompressedOffset
  1286. to retrieve the bitmap data. If PackingMethod is 1 seek to CompressedOffset,
  1287. and decode CompressedSize bytes using the RunLen algorithm:
  1288.   n=getc(f); if(n&0x80) copy n&0x7F bytes, else copy next byte n times.
  1289. If PackingMethod is 2 use the LZ77 algorithm described above and if Packing-
  1290. Method is 3 first use LZ77, then RunLen to decompress.
  1291.  
  1292. If PictureType is 8 the picture is a metafile described by:
  1293.  
  1294. compressed unsigned short MappingMode
  1295. unsigned short Width
  1296. unsigned short Height
  1297. compressed unsigned long DecompressedSize   can be used to allocate buffer
  1298. compressed unsigned long CompressedSize
  1299. compressed unsigned long HotspotSize        0 if none are defined
  1300. unsigned long CompressedOffset            relative to &PictureType
  1301. unsigned long HotspotOffset            relative to &PictureType
  1302.  
  1303. Seek to CompressedOffset and decompress CompressedSize bytes as described
  1304. above to retrieve metafile data.
  1305.  
  1306. If HotspotSize or HotspotOffset is 0, no hotspots are defined. Otherwise
  1307. seek to HotspotOffset and retrieve HotspotSize bytes of hotspot definition
  1308. as declared below. Each macro hotspot contributes data to MacroData in a
  1309. way not fully understood at this moment.
  1310.  
  1311. unsigned char Always1
  1312. unsigned short NumberOfHotspots
  1313. unsigned long SizeOfMacroData
  1314. struct
  1315. {
  1316.     unsigned char id0,id1,id2;
  1317.     unsigned short x,y,w,h;
  1318.     unsigned long hash;
  1319. }
  1320. Hotspot[NumberOfHotspots]
  1321. char MacroData[SizeOfMacroData]       if SizeOfMacroData>0 the first byte
  1322.                       of MacroData is always 2.
  1323. struct
  1324. {
  1325.     STRINGZ HotspotName
  1326.     STRINGZ ContextNameOrMacro
  1327. }
  1328. StringData[NumberOfHotspots]
  1329.  
  1330. Possible values of id0,id1,id2 are:
  1331. 0xC8 0x00 0x00    macro visible
  1332. 0xCC 0x04 0x00    macro invisible
  1333. 0xE2 0x00 0x00    popup jump visible
  1334. 0xE3 0x00 0x00    topic jump visible
  1335. 0xE6 0x04 0x00    popup jump invisible
  1336. 0xE7 0x04 0x00    topic jump invisible
  1337. 0xEA 0x00 0x00    popup jump into external file visible
  1338. 0xEB 0x00 0x00    topic jump into external file / secondary window visible
  1339. 0xEE 0x04 0x00    popup jump into external file invisible
  1340. 0xEF 0x04 0x00    topic jump into external file / secondary window invisible
  1341.  
  1342. The hash field is only used if id0 = 0xE2, 0xE3, 0xE6, 0xE7. It is 1 if
  1343. id0 = 0xC8 or 0xCC.
  1344. The ContextNameOrMacro contains a macro if id0 = 0xC8 or 0xCC, otherwise
  1345. it contains a ContextName (id0 = 0xE2, 0xE3, 0xE6, 0xE7) or the complete
  1346. reference ContextName>Window@File (id0 = 0xEA, 0xEB, 0xEE, 0xEF) (@File
  1347. may be missing if target is in same file).
  1348.  
  1349. Annotation file format
  1350.  
  1351. An annotation file created by WinHelp uses the same basic file format as
  1352. a Windows help file. The first 16 bytes contain the same header as a help
  1353. file, with same Magic. DirectoryStart points to a FILEHEADER of an internal
  1354. directory formatted the same way as a help file internal directory. There
  1355. are just internal files of different name and format used to collect the
  1356. annotations.
  1357.  
  1358. @VERSION
  1359.  
  1360. The first internal file described contains (after the usual FILEHEADER) 6
  1361. bytes of version info:
  1362. 0x08 0x62 0x6D 0x66 0x01 0x00           (I've never seen other values)
  1363.  
  1364. @LINK
  1365.  
  1366. The @LINK internal file contains (after the usual FILEHEADER) the number of
  1367. annotations and the TOPICOFFSET of every annotation. The TopicOffset separates
  1368. into a TopicBlockNumber in it's upper bits and TopicBlockOffset pointing into
  1369. the decompression buffer in it's lower bits as explained above in the
  1370. description of the |TOPIC format and points the the first TOPICLINK following
  1371. the TOPICHEADER of the topic where the annotation belongs to.
  1372.  
  1373. unsigned short NumberOfAnnotations
  1374. struct
  1375. {
  1376.     unsigned long TopicOffset
  1377.     unsigned long Unknown1      // always 0
  1378.     unsigned long Unknown2      // always 0
  1379. }
  1380. AnnotationTopicRef[NumberOfAnnotations]
  1381.  
  1382. n!0
  1383.  
  1384. For each annotation the ANN file also carrys an internal file with a name like
  1385. 12345!0, where 12345 is the decimal representation of the TopicOffset (as
  1386. listed in the @LINK array) where the annotation belongs to. These files
  1387. contain the annotation text as unformatted, uncompressed plain ANSI characters,
  1388. and are not NUL terminated.
  1389.  
  1390. That's all what I've seen in an annotation file.
  1391.  
  1392. *.CAC, *.AUX
  1393.  
  1394. Multimedia files using extensions *.CAC or *.AUX are formatted like helpfiles,
  1395. but contain only auxillary files, no |SYSTEM or |TOPIC.
  1396. Investigate them yourself. HELPDECO may be used to display or extract files
  1397. contained in them.
  1398.  
  1399. LZ77
  1400.  
  1401. You want to handle LZ77 compressed data in HLPs, MRBs, and SHGs yourself ?
  1402. Here is an algorithm to do it:
  1403.  
  1404. // LZ77 compression / decompression algorithm
  1405. // this is the compression Microsoft used in Windows *.HLP and *.MRB files
  1406.  
  1407. // so it works like Microsoft COMPRESS.EXE/EXPAND.EXE/LZEXPAND.DLL
  1408. //#define MSEXPAND
  1409.  
  1410. #include <stdio.h>
  1411. #include <stdlib.h>
  1412.  
  1413. #define N 4096
  1414. #define F 16
  1415. #define THRESHOLD 3
  1416.  
  1417. #define dad (node+1)
  1418. #define lson (node+1+N)
  1419. #define rson (node+1+N+N)
  1420. #define root (node+1+N+N+N)
  1421. #define NIL -1
  1422.  
  1423. char *buffer;
  1424. int *node;
  1425. int pos;
  1426.  
  1427. int insert(int i,int run)
  1428. {
  1429.     int c,j,k,l,n,match;
  1430.     int *p;
  1431.  
  1432.     k=l=1;
  1433.     match=THRESHOLD-1;
  1434.     p=&root[(unsigned char)buffer[i]];
  1435.     lson[i]=rson[i]=NIL;
  1436.     while((j=*p)!=NIL)
  1437.     {
  1438.     for(n=min(k,l);n<run&&(c=(buffer[j+n]-buffer[i+n]))==0;n++) ;
  1439.     if(n>match)
  1440.     {
  1441.         match=n;
  1442.         pos=j;
  1443.     }
  1444.     if(c<0)
  1445.     {
  1446.         p=&lson[j];
  1447.         k=n;
  1448.     }
  1449.     else if(c>0)
  1450.     {
  1451.         p=&rson[j];
  1452.         l=n;
  1453.     }
  1454.     else
  1455.     {
  1456.         dad[j]=NIL;
  1457.         dad[lson[j]]=lson+i-node;
  1458.         dad[rson[j]]=rson+i-node;
  1459.         lson[i]=lson[j];
  1460.         rson[i]=rson[j];
  1461.         break;
  1462.     }
  1463.     }
  1464.     dad[i]=p-node;
  1465.     *p=i;
  1466.     return match;
  1467. }
  1468.  
  1469. void delete(int z)
  1470. {
  1471.     int j;
  1472.  
  1473.     if(dad[z]!=NIL)
  1474.     {
  1475.     if(rson[z]==NIL)
  1476.     {
  1477.         j=lson[z];
  1478.     }
  1479.     else if(lson[z]==NIL)
  1480.     {
  1481.         j=rson[z];
  1482.     }
  1483.     else
  1484.     {
  1485.         j=lson[z];
  1486.         if(rson[j]!=NIL)
  1487.         {
  1488.         do
  1489.         {
  1490.             j=rson[j];
  1491.         }
  1492.         while(rson[j]!=NIL);
  1493.         node[dad[j]]=lson[j];
  1494.         dad[lson[j]]=dad[j];
  1495.         lson[j]=lson[z];
  1496.         dad[lson[z]]=lson+j-node;
  1497.         }
  1498.         rson[j]=rson[z];
  1499.         dad[rson[z]]=rson+j-node;
  1500.     }
  1501.     dad[j]=dad[z];
  1502.     node[dad[z]]=j;
  1503.     dad[z]=NIL;
  1504.     }
  1505. }
  1506.  
  1507. void compress(FILE *f,FILE *out)
  1508. {
  1509.     int ch,i,run,len,match,size,mask;
  1510.     char buf[17];
  1511.  
  1512.     buffer=malloc(N+F+(N+1+N+N+256)*sizeof(int)); // 28.5 k !
  1513.     if(buffer)
  1514.     {
  1515. #ifdef MSEXPAND
  1516.     struct { long magic, magic2; int magic3; long filesize; } header;
  1517.  
  1518.     header.magic=0x44445A53L; // SZDD
  1519.     header.magic2=0x3327F088L;
  1520.     header.magic3=0x0041;
  1521.     header.filesize=filelength(fileno(f));
  1522.     fwrite(&header,sizeof(header),1,out);
  1523. #endif
  1524.     node=(int *)(buffer+N+F);
  1525.     for(i=0;i<256;i++) root[i]=NIL;
  1526.     for(i=NIL;i<N;i++) dad[i]=NIL;
  1527.     size=mask=1;
  1528.     buf[0]=0;
  1529.     i=N-F-F;
  1530.     for(len=0;len<F&&(ch=getc(f))!=-1;len++)
  1531.     {
  1532.         buffer[i+F]=ch;
  1533.         i=(i+1)&(N-1);
  1534.     }
  1535.     run=len;
  1536.     do
  1537.     {
  1538.         ch=getc(f);
  1539.         if(i>=N-F)
  1540.         {
  1541.         delete(i+F-N);
  1542.         buffer[i+F]=buffer[i+F-N]=ch;
  1543.         }
  1544.         else
  1545.         {
  1546.         delete(i+F);
  1547.         buffer[i+F]=ch;
  1548.         }
  1549.         match=insert(i,run);
  1550.         if(ch==-1)
  1551.         {
  1552.         run--;
  1553.         len--;
  1554.         }
  1555.         if(len++>=run)
  1556.         {
  1557.         if(match>=THRESHOLD)
  1558.         {
  1559. #ifdef MSEXPAND
  1560.             buf[size++]=pos;
  1561.             buf[size++]=((pos>>4)&0xF0)+(match-3);
  1562. #else
  1563.             buf[0]|=mask;
  1564.             *(int *)(buf+size)=((match-3)<<12)|((i-pos-1)&(N-1));
  1565.             size+=2;
  1566. #endif
  1567.             len-=match;
  1568.         }
  1569.         else
  1570.         {
  1571. #ifdef MSEXPAND
  1572.             buf[0]|=mask;
  1573. #endif
  1574.             buf[size++]=buffer[i];
  1575.             len--;
  1576.         }
  1577.         if(!((mask+=mask)&0xFF))
  1578.         {
  1579.             fwrite(buf,size,1,out);
  1580.             size=mask=1;
  1581.             buf[0]=0;
  1582.         }
  1583.         }
  1584.         i=(i+1)&(N-1);
  1585.     }
  1586.     while(len>0);
  1587.     if(size>1) fwrite(buf,size,1,out);
  1588.     free(buffer);
  1589.     }
  1590. }
  1591.  
  1592. void expand(FILE *f,FILE *out)
  1593. {
  1594.     int bits,ch,i,j,len,mask;
  1595.     char *buffer;
  1596.  
  1597. #ifdef MSEXPAND
  1598.     struct { long magic, magic2; int magic3; long filesize; } header;
  1599.  
  1600.     i=fread(&header,1,sizeof(header),f);
  1601.     if(i!=sizeof(header)||header.magic!=0x44445A53L||header.magic2!=0x3327F088L||header.magic3!=0x0041)
  1602.     {
  1603.     fwrite(&header,1,i,out);
  1604.     while((ch=getc(f))!=-1) putc(ch,out);
  1605.     return;
  1606.     }
  1607. #endif
  1608.     buffer=malloc(N);
  1609.     if(buffer)
  1610.     {
  1611.     i=N-F;
  1612.     while((bits=getc(f))!=-1)
  1613.     {
  1614.         for(mask=0x01;mask&0xFF;mask<<=1)
  1615.         {
  1616. #ifdef MSEXPAND
  1617.         if(!(bits&mask))
  1618.         {
  1619.             j=getc(f);
  1620.             if(j==-1) break;
  1621.             len=getc(f);
  1622.             j+=(len&0xF0)<<4;
  1623.             len=(len&15)+3;
  1624. #else
  1625.         if(bits&mask)
  1626.         {
  1627.             j=getw(f);
  1628.             len=((j>>12)&15)+3;
  1629.             j=(i-j-1)&(N-1);
  1630. #endif
  1631.             while(len--)
  1632.             {
  1633.             putc(buffer[i]=buffer[j],out);
  1634.             j=(j+1)&(N-1);
  1635.             i=(i+1)&(N-1);
  1636.             }
  1637.         }
  1638.         else
  1639.         {
  1640.             ch=getc(f);
  1641. #ifndef MSEXPAND
  1642.             if(ch==-1) break;
  1643. #endif
  1644.             putc(buffer[i]=ch,out);
  1645.             i=(i+1)&(N-1);
  1646.         }
  1647.         }
  1648.     }
  1649.     free(buffer);
  1650.     }
  1651. }
  1652.  
  1653. That's all I can tell you about the format of Windows 3.x/95 help files.
  1654. If you found out more, please let me know.
  1655.  
  1656. M. Winterhoff
  1657. 100326.2776@compuserve.com
  1658.